// Copyright (C) Kumo inc. and its affiliates.
// Author: Jeff.li lijippy@163.com
// All rights reserved.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.
//
#pragma once

#include <cantor/proto/type.pb.h>
#include <turbo/crypto/md5.h>
#include <cantor/type/udf_registry.h>

namespace cantor {
    //////////////////////////////////////////////////////////////////////////////////////
    ///         promote_cost
    /// Calculate the promotion cost from one type to another.
    ///
    /// @param from  The source type to be promoted.
    /// @param to    The target type to promote to.
    ///
    /// @return
    ///   0   - No conversion needed (types are the same).
    ///   >0  - Promotion required; the value indicates the promotion level/cost.
    ///   -1  - Both types are promotable, but conversion from `from` to `to` is not allowed
    ///         (e.g., `from` is float, `to` is int).
    ///   -2  - `from` type cannot be promoted.
    ///   -3  - `to` type cannot be promoted.
    ///   <0  - General failure indicating promotion is not possible.
    //////////////////////////////////////////////////////////////////////////////////////

    int32_t promote_cost(const pb::TypeInfo &from, const pb::TypeInfo &to);

    bool type_equal(const pb::TypeInfo &from, const pb::TypeInfo &to);

    template<pb::TypeKind From, pb::TypeKind To>
    struct ConvertCostTrait {
        static constexpr int cost = -2;
    };

    /////////////////////////////////////////////////////////////////////////////////////////
    /// ConvertCostTrait: defines conversion cost from one numeric
    /// type to another. Lower cost means easier conversion.
    /////////////////////////////////////////////////////////////////////////////////////////
    /// ------------------ FROM INT8 ------------------

    // clang-format off
    template <> struct ConvertCostTrait<pb::TYPE_KIND_NONE, pb::TYPE_KIND_NONE> { static constexpr int cost = 0; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_NONE, pb::TYPE_KIND_INT8> { static constexpr int cost = -2; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_NONE, pb::TYPE_KIND_INT16> { static constexpr int cost = -2; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_NONE, pb::TYPE_KIND_INT32> { static constexpr int cost = -2; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_NONE, pb::TYPE_KIND_INT64> { static constexpr int cost = -2; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_NONE, pb::TYPE_KIND_INT128> { static constexpr int cost = -2; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_NONE, pb::TYPE_KIND_UINT8> { static constexpr int cost = -2; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_NONE, pb::TYPE_KIND_UINT16> { static constexpr int cost = -2; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_NONE, pb::TYPE_KIND_UINT32> { static constexpr int cost = -2; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_NONE, pb::TYPE_KIND_UINT64> { static constexpr int cost = -2; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_NONE, pb::TYPE_KIND_UINT128> { static constexpr int cost = -2; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_NONE, pb::TYPE_KIND_FP8> { static constexpr int cost = -2; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_NONE, pb::TYPE_KIND_FP16> { static constexpr int cost = -2; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_NONE, pb::TYPE_KIND_FP32> { static constexpr int cost = -2; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_NONE, pb::TYPE_KIND_FP64> { static constexpr int cost = -2; };
    // ------------------ FROM INT8 ------------------
    template <> struct ConvertCostTrait<pb::TYPE_KIND_INT8, pb::TYPE_KIND_INT8> { static constexpr int cost = 0; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_INT8, pb::TYPE_KIND_INT16> { static constexpr int cost = 1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_INT8, pb::TYPE_KIND_INT32> { static constexpr int cost = 2; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_INT8, pb::TYPE_KIND_INT64> { static constexpr int cost = 3; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_INT8, pb::TYPE_KIND_INT128> { static constexpr int cost = 4; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_INT8, pb::TYPE_KIND_UINT8> { static constexpr int cost = 1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_INT8, pb::TYPE_KIND_UINT16> { static constexpr int cost = 2; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_INT8, pb::TYPE_KIND_UINT32> { static constexpr int cost = 3; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_INT8, pb::TYPE_KIND_UINT64> { static constexpr int cost = 4; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_INT8, pb::TYPE_KIND_UINT128> { static constexpr int cost = 5; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_INT8, pb::TYPE_KIND_FP8> { static constexpr int cost = -1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_INT8, pb::TYPE_KIND_FP16> { static constexpr int cost = -1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_INT8, pb::TYPE_KIND_FP32> { static constexpr int cost = 5; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_INT8, pb::TYPE_KIND_FP64> { static constexpr int cost = 6; };

    // ------------------ FROM INT16 ------------------
    template <> struct ConvertCostTrait<pb::TYPE_KIND_INT16, pb::TYPE_KIND_INT8> { static constexpr int cost = -1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_INT16, pb::TYPE_KIND_INT16> { static constexpr int cost = 0; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_INT16, pb::TYPE_KIND_INT32> { static constexpr int cost = 1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_INT16, pb::TYPE_KIND_INT64> { static constexpr int cost = 2; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_INT16, pb::TYPE_KIND_INT128> { static constexpr int cost = 3; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_INT16, pb::TYPE_KIND_UINT8> { static constexpr int cost = -1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_INT16, pb::TYPE_KIND_UINT16> { static constexpr int cost = 1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_INT16, pb::TYPE_KIND_UINT32> { static constexpr int cost = 2; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_INT16, pb::TYPE_KIND_UINT64> { static constexpr int cost = 3; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_INT16, pb::TYPE_KIND_UINT128> { static constexpr int cost = 4; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_INT16, pb::TYPE_KIND_FP8> { static constexpr int cost = -1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_INT16, pb::TYPE_KIND_FP16> { static constexpr int cost = -1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_INT16, pb::TYPE_KIND_FP32> { static constexpr int cost = 4; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_INT16, pb::TYPE_KIND_FP64> { static constexpr int cost = 5; };

    // ------------------ FROM INT32 ------------------
    template <> struct ConvertCostTrait<pb::TYPE_KIND_INT32, pb::TYPE_KIND_INT8> { static constexpr int cost = -1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_INT32, pb::TYPE_KIND_INT16> { static constexpr int cost = -1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_INT32, pb::TYPE_KIND_INT32> { static constexpr int cost = 0; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_INT32, pb::TYPE_KIND_INT64> { static constexpr int cost = 1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_INT32, pb::TYPE_KIND_INT128> { static constexpr int cost = 2; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_INT32, pb::TYPE_KIND_UINT8> { static constexpr int cost = -1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_INT32, pb::TYPE_KIND_UINT16> { static constexpr int cost = -1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_INT32, pb::TYPE_KIND_UINT32> { static constexpr int cost = 1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_INT32, pb::TYPE_KIND_UINT64> { static constexpr int cost = 2; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_INT32, pb::TYPE_KIND_UINT128> { static constexpr int cost = 3; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_INT32, pb::TYPE_KIND_FP8> { static constexpr int cost = -1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_INT32, pb::TYPE_KIND_FP16> { static constexpr int cost = -1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_INT32, pb::TYPE_KIND_FP32> { static constexpr int cost = 5; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_INT32, pb::TYPE_KIND_FP64> { static constexpr int cost = 6; };

    // ------------------ FROM INT64 ------------------
    template <> struct ConvertCostTrait<pb::TYPE_KIND_INT64, pb::TYPE_KIND_INT8> { static constexpr int cost = -1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_INT64, pb::TYPE_KIND_INT16> { static constexpr int cost = -1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_INT64, pb::TYPE_KIND_INT32> { static constexpr int cost = -1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_INT64, pb::TYPE_KIND_INT64> { static constexpr int cost = 0; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_INT64, pb::TYPE_KIND_INT128> { static constexpr int cost = 1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_INT64, pb::TYPE_KIND_UINT8> { static constexpr int cost = -1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_INT64, pb::TYPE_KIND_UINT16> { static constexpr int cost = -1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_INT64, pb::TYPE_KIND_UINT32> { static constexpr int cost = -1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_INT64, pb::TYPE_KIND_UINT64> { static constexpr int cost = 1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_INT64, pb::TYPE_KIND_UINT128> { static constexpr int cost = 2; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_INT64, pb::TYPE_KIND_FP8> { static constexpr int cost = -1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_INT64, pb::TYPE_KIND_FP16> { static constexpr int cost = -1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_INT64, pb::TYPE_KIND_FP32> { static constexpr int cost = 5; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_INT64, pb::TYPE_KIND_FP64> { static constexpr int cost = 6; };

    // ------------------ FROM INT128 ------------------
    template <> struct ConvertCostTrait<pb::TYPE_KIND_INT128, pb::TYPE_KIND_INT8> { static constexpr int cost = -1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_INT128, pb::TYPE_KIND_INT16> { static constexpr int cost = -1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_INT128, pb::TYPE_KIND_INT32> { static constexpr int cost = -1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_INT128, pb::TYPE_KIND_INT64> { static constexpr int cost = -1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_INT128, pb::TYPE_KIND_INT128> { static constexpr int cost = 0; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_INT128, pb::TYPE_KIND_UINT8> { static constexpr int cost = -1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_INT128, pb::TYPE_KIND_UINT16> { static constexpr int cost = -1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_INT128, pb::TYPE_KIND_UINT32> { static constexpr int cost = -1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_INT128, pb::TYPE_KIND_UINT64> { static constexpr int cost = -1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_INT128, pb::TYPE_KIND_UINT128> { static constexpr int cost = 1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_INT128, pb::TYPE_KIND_FP8> { static constexpr int cost = -1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_INT128, pb::TYPE_KIND_FP16> { static constexpr int cost = -1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_INT128, pb::TYPE_KIND_FP32> { static constexpr int cost = 5; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_INT128, pb::TYPE_KIND_FP64> { static constexpr int cost = 6; };
    // ------------------ FROM UINT8 ------------------
    template <> struct ConvertCostTrait<pb::TYPE_KIND_UINT8, pb::TYPE_KIND_INT8> { static constexpr int cost = -1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_UINT8, pb::TYPE_KIND_INT16> { static constexpr int cost = -1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_UINT8, pb::TYPE_KIND_INT32> { static constexpr int cost = -1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_UINT8, pb::TYPE_KIND_INT64> { static constexpr int cost = -1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_UINT8, pb::TYPE_KIND_INT128> { static constexpr int cost = -1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_UINT8, pb::TYPE_KIND_UINT8> { static constexpr int cost = 0; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_UINT8, pb::TYPE_KIND_UINT16> { static constexpr int cost = 1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_UINT8, pb::TYPE_KIND_UINT32> { static constexpr int cost = 2; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_UINT8, pb::TYPE_KIND_UINT64> { static constexpr int cost = 3; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_UINT8, pb::TYPE_KIND_UINT128> { static constexpr int cost = 4; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_UINT8, pb::TYPE_KIND_FP8> { static constexpr int cost = -1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_UINT8, pb::TYPE_KIND_FP16> { static constexpr int cost = -1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_UINT8, pb::TYPE_KIND_FP32> { static constexpr int cost = 5; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_UINT8, pb::TYPE_KIND_FP64> { static constexpr int cost = 6; };
    // ------------------ FROM UINT16 ------------------
    template <> struct ConvertCostTrait<pb::TYPE_KIND_UINT16, pb::TYPE_KIND_INT8> { static constexpr int cost = -1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_UINT16, pb::TYPE_KIND_INT16> { static constexpr int cost = -1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_UINT16, pb::TYPE_KIND_INT32> { static constexpr int cost = -1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_UINT16, pb::TYPE_KIND_INT64> { static constexpr int cost = -1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_UINT16, pb::TYPE_KIND_INT128> { static constexpr int cost = -1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_UINT16, pb::TYPE_KIND_UINT8> { static constexpr int cost = -1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_UINT16, pb::TYPE_KIND_UINT16> { static constexpr int cost = 0; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_UINT16, pb::TYPE_KIND_UINT32> { static constexpr int cost = 1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_UINT16, pb::TYPE_KIND_UINT64> { static constexpr int cost = 2; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_UINT16, pb::TYPE_KIND_UINT128> { static constexpr int cost = 3; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_UINT16, pb::TYPE_KIND_FP8> { static constexpr int cost = -1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_UINT16, pb::TYPE_KIND_FP16> { static constexpr int cost = -1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_UINT16, pb::TYPE_KIND_FP32> { static constexpr int cost = 5; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_UINT16, pb::TYPE_KIND_FP64> { static constexpr int cost = 6; };
    // ------------------ FROM UINT32 ------------------
    template <> struct ConvertCostTrait<pb::TYPE_KIND_UINT32, pb::TYPE_KIND_INT8> { static constexpr int cost = -1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_UINT32, pb::TYPE_KIND_INT16> { static constexpr int cost = -1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_UINT32, pb::TYPE_KIND_INT32> { static constexpr int cost = -1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_UINT32, pb::TYPE_KIND_INT64> { static constexpr int cost = -1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_UINT32, pb::TYPE_KIND_INT128> { static constexpr int cost = -1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_UINT32, pb::TYPE_KIND_UINT8> { static constexpr int cost = -1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_UINT32, pb::TYPE_KIND_UINT16> { static constexpr int cost = -1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_UINT32, pb::TYPE_KIND_UINT32> { static constexpr int cost = 0; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_UINT32, pb::TYPE_KIND_UINT64> { static constexpr int cost = 1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_UINT32, pb::TYPE_KIND_UINT128> { static constexpr int cost = 2; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_UINT32, pb::TYPE_KIND_FP8> { static constexpr int cost = -1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_UINT32, pb::TYPE_KIND_FP16> { static constexpr int cost = -1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_UINT32, pb::TYPE_KIND_FP32> { static constexpr int cost = 5; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_UINT32, pb::TYPE_KIND_FP64> { static constexpr int cost = 6; };
    // ------------------ FROM UINT64 ------------------
    template <> struct ConvertCostTrait<pb::TYPE_KIND_UINT64, pb::TYPE_KIND_INT8> { static constexpr int cost = -1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_UINT64, pb::TYPE_KIND_INT16> { static constexpr int cost = -1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_UINT64, pb::TYPE_KIND_INT32> { static constexpr int cost = -1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_UINT64, pb::TYPE_KIND_INT64> { static constexpr int cost = -1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_UINT64, pb::TYPE_KIND_INT128> { static constexpr int cost = -1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_UINT64, pb::TYPE_KIND_UINT8> { static constexpr int cost = -1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_UINT64, pb::TYPE_KIND_UINT16> { static constexpr int cost = -1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_UINT64, pb::TYPE_KIND_UINT32> { static constexpr int cost = -1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_UINT64, pb::TYPE_KIND_UINT64> { static constexpr int cost = 0; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_UINT64, pb::TYPE_KIND_UINT128> { static constexpr int cost = 1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_UINT64, pb::TYPE_KIND_FP8> { static constexpr int cost = -1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_UINT64, pb::TYPE_KIND_FP16> { static constexpr int cost = -1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_UINT64, pb::TYPE_KIND_FP32> { static constexpr int cost = 5; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_UINT64, pb::TYPE_KIND_FP64> { static constexpr int cost = 6; };
    // ------------------ FROM UINT128 ------------------
    template <> struct ConvertCostTrait<pb::TYPE_KIND_UINT128, pb::TYPE_KIND_INT8> { static constexpr int cost = -1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_UINT128, pb::TYPE_KIND_INT16> { static constexpr int cost = -1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_UINT128, pb::TYPE_KIND_INT32> { static constexpr int cost = -1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_UINT128, pb::TYPE_KIND_INT64> { static constexpr int cost = -1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_UINT128, pb::TYPE_KIND_INT128> { static constexpr int cost = -1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_UINT128, pb::TYPE_KIND_UINT8> { static constexpr int cost = -1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_UINT128, pb::TYPE_KIND_UINT16> { static constexpr int cost = -1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_UINT128, pb::TYPE_KIND_UINT32> { static constexpr int cost = -1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_UINT128, pb::TYPE_KIND_UINT64> { static constexpr int cost = -1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_UINT128, pb::TYPE_KIND_UINT128> { static constexpr int cost = 0; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_UINT128, pb::TYPE_KIND_FP8> { static constexpr int cost = -1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_UINT128, pb::TYPE_KIND_FP16> { static constexpr int cost = -1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_UINT128, pb::TYPE_KIND_FP32> { static constexpr int cost = 5; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_UINT128, pb::TYPE_KIND_FP64> { static constexpr int cost = 6; };
    // ------------------ FROM FP8 ------------------
    template <> struct ConvertCostTrait<pb::TYPE_KIND_FP8, pb::TYPE_KIND_INT8> { static constexpr int cost = -2; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_FP8, pb::TYPE_KIND_INT16> { static constexpr int cost = -2; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_FP8, pb::TYPE_KIND_INT32> { static constexpr int cost = -2; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_FP8, pb::TYPE_KIND_INT64> { static constexpr int cost = -2; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_FP8, pb::TYPE_KIND_INT128> { static constexpr int cost = -2; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_FP8, pb::TYPE_KIND_UINT8> { static constexpr int cost = -2; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_FP8, pb::TYPE_KIND_UINT16> { static constexpr int cost = -2; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_FP8, pb::TYPE_KIND_UINT32> { static constexpr int cost = -2; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_FP8, pb::TYPE_KIND_UINT64> { static constexpr int cost = -2; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_FP8, pb::TYPE_KIND_UINT128> { static constexpr int cost = -2; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_FP8, pb::TYPE_KIND_FP8> { static constexpr int cost = -2; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_FP8, pb::TYPE_KIND_FP16> { static constexpr int cost = -2; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_FP8, pb::TYPE_KIND_FP32> { static constexpr int cost = -2; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_FP8, pb::TYPE_KIND_FP64> { static constexpr int cost = -2; };
    // ------------------ FROM FP16 ------------------
    template <> struct ConvertCostTrait<pb::TYPE_KIND_FP16, pb::TYPE_KIND_INT8> { static constexpr int cost = -2; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_FP16, pb::TYPE_KIND_INT16> { static constexpr int cost = -2; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_FP16, pb::TYPE_KIND_INT32> { static constexpr int cost = -2; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_FP16, pb::TYPE_KIND_INT64> { static constexpr int cost = -2; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_FP16, pb::TYPE_KIND_INT128> { static constexpr int cost = -2; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_FP16, pb::TYPE_KIND_UINT8> { static constexpr int cost = -2; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_FP16, pb::TYPE_KIND_UINT16> { static constexpr int cost = -2; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_FP16, pb::TYPE_KIND_UINT32> { static constexpr int cost = -2; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_FP16, pb::TYPE_KIND_UINT64> { static constexpr int cost = -2; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_FP16, pb::TYPE_KIND_UINT128> { static constexpr int cost = -2; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_FP16, pb::TYPE_KIND_FP8> { static constexpr int cost = -2; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_FP16, pb::TYPE_KIND_FP16> { static constexpr int cost = -2; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_FP16, pb::TYPE_KIND_FP32> { static constexpr int cost = -2; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_FP16, pb::TYPE_KIND_FP64> { static constexpr int cost = -2; };
    // ------------------ FROM FP32 ------------------
    template <> struct ConvertCostTrait<pb::TYPE_KIND_FP32, pb::TYPE_KIND_INT8> { static constexpr int cost = -1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_FP32, pb::TYPE_KIND_INT16> { static constexpr int cost = -1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_FP32, pb::TYPE_KIND_INT32> { static constexpr int cost = -1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_FP32, pb::TYPE_KIND_INT64> { static constexpr int cost = -1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_FP32, pb::TYPE_KIND_INT128> { static constexpr int cost = -1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_FP32, pb::TYPE_KIND_UINT8> { static constexpr int cost = -1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_FP32, pb::TYPE_KIND_UINT16> { static constexpr int cost = -1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_FP32, pb::TYPE_KIND_UINT32> { static constexpr int cost = -1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_FP32, pb::TYPE_KIND_UINT64> { static constexpr int cost = -1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_FP32, pb::TYPE_KIND_UINT128> { static constexpr int cost = -1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_FP32, pb::TYPE_KIND_FP8> { static constexpr int cost = -1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_FP32, pb::TYPE_KIND_FP16> { static constexpr int cost = -1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_FP32, pb::TYPE_KIND_FP32> { static constexpr int cost = 0; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_FP32, pb::TYPE_KIND_FP64> { static constexpr int cost = 1; };
    // ------------------ FROM FP64 ------------------
    template <> struct ConvertCostTrait<pb::TYPE_KIND_FP64, pb::TYPE_KIND_INT8> { static constexpr int cost = -1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_FP64, pb::TYPE_KIND_INT16> { static constexpr int cost = -1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_FP64, pb::TYPE_KIND_INT32> { static constexpr int cost = -1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_FP64, pb::TYPE_KIND_INT64> { static constexpr int cost = -1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_FP64, pb::TYPE_KIND_INT128> { static constexpr int cost = -1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_FP64, pb::TYPE_KIND_UINT8> { static constexpr int cost = -1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_FP64, pb::TYPE_KIND_UINT16> { static constexpr int cost = -1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_FP64, pb::TYPE_KIND_UINT32> { static constexpr int cost = -1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_FP64, pb::TYPE_KIND_UINT64> { static constexpr int cost = -1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_FP64, pb::TYPE_KIND_UINT128> { static constexpr int cost = -1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_FP64, pb::TYPE_KIND_FP8> { static constexpr int cost = -1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_FP64, pb::TYPE_KIND_FP16> { static constexpr int cost = -1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_FP64, pb::TYPE_KIND_FP32> { static constexpr int cost = -1; };
    template <> struct ConvertCostTrait<pb::TYPE_KIND_FP64, pb::TYPE_KIND_FP64> { static constexpr int cost = 0; };
    // clang-format on
    // ... continue for UINT8 -> all, UINT16 -> all, ... FP8 -> all, etc.
} // namespace cantor
